package me.illtamer.serializer;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import me.illtamer.rpc.entity.RpcRequest;
import me.illtamer.rpc.enumeration.SerializerCode;
import me.illtamer.rpc.exception.SerializeException;

import java.io.IOException;

/**
 * Json序列化器
 * */
@Slf4j
public class JsonSerializer implements CommonSerializer {
    // ObjectMapper 线程安全s
    private final ObjectMapper mapper = new ObjectMapper();

    @Override
    public byte[] serialize(Object object) {
        try {
            return mapper.writeValueAsBytes(object);
        } catch (JsonProcessingException e) {
            throw new SerializeException("序列化时有错误发生");
        }
    }

    @Override
    public Object deserialize(byte[] bytes, Class<?> clazz) {
        try {
            Object object = mapper.readValue(bytes, clazz);
            if (object instanceof RpcRequest) {
                object = handleRequest(object);
            }
            return object;
        } catch (IOException e) {
            throw new SerializeException("反序列化时有错误发生");
        }
    }

    /**
     * 由于使用JSON序列化和反序列化 {@link RpcRequest} 携带的参数数组，
     * 无法保证反序列化后仍然为原实例类，需要重新判断处理
     * */
    private Object handleRequest(Object object) throws IOException {
        RpcRequest rpcRequest = (RpcRequest) object;
        for (int i = 0; i < rpcRequest.getParamTypes().length; ++ i) {
            Class<?> clazz = rpcRequest.getParamTypes()[i];
            if (!clazz.isAssignableFrom(rpcRequest.getParameters()[i].getClass())) {
                // Object -> JsonString -> byte[] -> (clazz) Object
                byte[] bytes = mapper.writeValueAsBytes(rpcRequest.getParameters()[i]);
                rpcRequest.getParameters()[i] = mapper.readValue(bytes, clazz);
            }
        }
        return rpcRequest;
    }

    @Override
    public int getCode() {
        return SerializerCode.valueOf("JSON").getCode();
    }
}
